package com.zlc.workflow.core.instance.cmd;

import com.zlc.common.core.process.model.vo.*;
import com.zlc.common.core.process.service.*;
import com.zlc.common.core.user.model.vo.MayanPlatUserVo;
import com.zlc.common.core.user.service.MayanPlatUserService;
import com.zlc.common.util.*;
import com.zlc.workflow.constant.EventTimeEnum;
import com.zlc.workflow.constant.ProcessLogTypeEnum;
import com.zlc.workflow.core.instance.model.BuildFlowParam;
import com.zlc.workflow.core.instance.model.EventParam;
import com.zlc.workflow.core.instance.model.WorkflowDetail;
import com.zlc.workflow.core.instance.model.WorkflowTask;
import com.zlc.workflow.core.instance.service.EventService;
import com.zlc.workflow.flow.NodeModel;
import com.zlc.workflow.flow.NodeUtils;
import com.zlc.workflow.flow.node.TaskNode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.zlc.common.common.constant.WorkflowConstant.*;

/**
 * 创建流程命令
 */
@Slf4j
public class BuildWorkflowCmd extends AbstractCmd<WorkflowDetail> {

    @Autowired
    private MayanPlatUserService mayanPlatUserService;
    @Autowired
    private StoneProcessService stoneProcessService;
    @Autowired
    private StoneProcessInstanceService stoneProcessInstanceService;
    @Autowired
    private StoneProcessInstanceHistoryService stoneProcessInstanceHistoryService;
    @Autowired
    private StoneTaskService stoneTaskService;
    @Autowired
    private StoneTaskHistoryService stoneTaskHistoryService;
    @Autowired
    private StoneProcessLogService stoneProcessLogService;
    @Autowired
    private EventService eventService;
    @Autowired
    private StoneProcessVariableConfigService stoneProcessVariableConfigService;
    @Autowired
    private StoneProcessNodeService stoneProcessNodeService;

    private final BuildFlowParam workflowParam;

    private final String account;

    private final String accountName;

    private final String processId;

    private final MayanPlatUserVo nowUser;

    private String processInstanceId;

    private final Map<String, Object> param;

    /**
     * 配置变量
     */
    private Map<String, String> variableConfigs = new HashMap<>();

    private final WorkflowDetail result = new WorkflowDetail();

    private StoneProcessVo processVo;


    /**
     * 获取当前命令对象的所有带 @Autowired注解的属性，然后从spring上下文中获取到注入进去
     *
     * 流程实例表
     * 任务实例表
     */
    public BuildWorkflowCmd(Object params) {
        super(params);
        this.workflowParam = (BuildFlowParam) params;
        this.processId = workflowParam.getProcessId();
        this.account = workflowParam.getAccount();
        this.processInstanceId = workflowParam.getProcessInstanceId();
        if (ObjectUtils.isEmpty(processInstanceId)) {
            processInstanceId = SnowflakeUtils.next();
        }
        result.setProcessInstanceId(processInstanceId);
        this.param = workflowParam.getFormData();
        AssertUtils.isTrue(!ObjectUtils.isEmpty(workflowParam.getAccount()), "操作人不可为空");
        this.nowUser = mayanPlatUserService.queryByUserAccount(workflowParam.getAccount());
        AssertUtils.isTrue(!ObjectUtils.isEmpty(this.nowUser), "操作人不存在");
        this.accountName = nowUser.getUsername();
    }

    @Override
    public WorkflowDetail execute() {
        this.processVo = stoneProcessService.getByProcessId(processId);
        AssertUtils.isTrue(!ObjectUtils.isEmpty(processVo), "无效的流程-" + processId);
        //流程内容
        String content = NodeUtils.getProcessContent(processVo.getContent());
        //第一个任务节点
        TaskNode firstTask = NodeUtils.getFirstTask(content, processId);
        StoneProcessNodeVo nodeConfig = stoneProcessNodeService.getByNodeId(this.processId, firstTask.getNodeId());
        //保存流程实例
        saveProcessInstance(nodeConfig);
        //保存任务实例
        saveTaskVo(firstTask, nodeConfig);
        //执行事件操作
        eventExecute();
        processStartEvent();
        return result;
    }

    private void eventExecute() {
        WorkflowTask details = this.result.getDetails();
        EventParam eventParam = BeanCopyUtils.copyObj(details, EventParam.class);
        eventParam.setActorId(this.account);
        eventParam.setActorName(this.accountName);
        eventParam.setEventTime(EVENT_TASK_ARRIVE);
        eventParam.setFormData(this.param);
        eventParam.setFirstTask(true);
        eventParam.setVariableConfigs(variableConfigs);
        eventService.execute(eventParam);
    }

    private void processStartEvent() {
        WorkflowTask details = this.result.getDetails();
        EventParam eventParam = BeanCopyUtils.copyObj(details, EventParam.class);
        eventParam.setActorId(this.account);
        eventParam.setActorName(this.accountName);
        eventParam.setEventTime(EventTimeEnum.EVENT_PROCESS_START.getType());
        eventParam.setFormData(this.param);
        eventParam.setFirstTask(true);
        eventParam.setVariableConfigs(variableConfigs);
        eventService.execute(eventParam);
    }

    private void saveProcessInstance(StoneProcessNodeVo nodeConfig) {
        //保存流程实例信息
        StoneProcessInstanceVo instanceVo = new StoneProcessInstanceVo();
        instanceVo.setProcessInstanceId(processInstanceId);
        instanceVo.setModule(this.processVo.getModule());
        instanceVo.setSystem(this.processVo.getSystem());
        instanceVo.setExt(JsonHelper.toJson(this.param));
        instanceVo.setProcessInstanceName(processInstanceName(this.processVo.getProcessName()));
        instanceVo.setProcessId(processId);
        instanceVo.setStarter(account);
        instanceVo.setStarterName(nowUser.getUsername());
        instanceVo.setStatus(INSTANCE_STATUS_DRAFT);
        instanceVo.setStartTime(new Date());
        if (!ObjectUtils.isEmpty(nodeConfig)) {
            instanceVo.setUrl(nodeConfig.getExecForm());
        }
        stoneProcessInstanceService.saveVo(instanceVo);
        WorkflowTask task = BeanCopyUtils.copyObj(instanceVo, WorkflowTask.class);

        StoneProcessInstanceHistoryVo historyVo = BeanCopyUtils.copyAndInit(instanceVo, StoneProcessInstanceHistoryVo.class);
        stoneProcessInstanceHistoryService.saveVo(historyVo);
        this.result.setDetails(task);
    }

    private void saveTaskVo(TaskNode firstTask, StoneProcessNodeVo nodeConfig) {

        //保存流程任务记录
        StoneTaskVo taskVo = new StoneTaskVo();
        taskVo.setProcessInstanceId(processInstanceId);
        taskVo.setTaskId(PlatUtils.uuid());
        taskVo.setActorId(account);
        taskVo.setActorName(nowUser.getUsername());
        taskVo.setExt(JsonHelper.toJson(this.param));
        taskVo.setStatus(TASK_STATUS_PROCESSING);
        taskVo.setNodeId(firstTask.nodeId());
        taskVo.setNodeName(firstTask.getNodeName());
        taskVo.setNodeType(firstTask.nodeType().getValue());
        taskVo.setProcessId(processId);
        taskVo.setReceiveTime(new Date());
        if (!ObjectUtils.isEmpty(nodeConfig)) {
            taskVo.setUrl(nodeConfig.getExecForm());
        }
        //路线
        List<NodeModel> pre = firstTask.pre();
        if (!ObjectUtils.isEmpty(pre)) {
            String pathNodeId = pre.get(0).path().get(0).nodeId();
            taskVo.setPath(pathNodeId);
        }
        stoneTaskService.saveVo(taskVo);
        //保存任务历史记录
        StoneTaskHistoryVo taskHistoryVo = BeanCopyUtils.copyAndInit(taskVo, StoneTaskHistoryVo.class);
        stoneTaskHistoryService.saveVo(taskHistoryVo);

        //记录日志
        saveLog(taskVo, firstTask);

        WorkflowTask details = this.result.getDetails();
        if (!ObjectUtils.isEmpty(details)) {
            details.setTaskStatus(taskVo.getStatus());
            details.setProcessCode(processVo.getProcessCode());
            details.setCurAccount(account);
            details.setProcessName(processVo.getProcessName());
            details.setInitAccount(account);
            details.setInitUserName(nowUser.getUsername());
            details.setTaskId(taskVo.getTaskId());
            details.setReceiveTime(taskVo.getReceiveTime());
            details.setNodeId(firstTask.nodeId());
            details.setNodeName(firstTask.nodeName());
            details.setNodeNum(firstTask.nodeNum());
            details.setNodeType(firstTask.nodeType().value());
        }
        //获取配置变量
        this.variableConfigs = stoneProcessVariableConfigService.getVariableConfigs(processId, firstTask.nodeId());
    }

    private void saveLog(StoneTaskVo taskVo, TaskNode firstTask) {
        //记录日志
        StoneProcessLogVo logVo = new StoneProcessLogVo();
        logVo.setProcessInstanceId(processInstanceId);
        logVo.setProcessId(processId);
        logVo.setNodeName("开始");
        logVo.setOperateUser(account);
        logVo.setOperateUserName(nowUser.getUsername());
        logVo.setTaskId(taskVo.getTaskId());
        logVo.setOperateType(ProcessLogTypeEnum.BUILD.getType());
        logVo.setLogContent("创建流程【" + processVo.getProcessName() + "】");
        stoneProcessLogService.saveVo(logVo);
    }

    private String processInstanceName(String processName) {
        Object processInstanceName = this.param.get("processInstanceName");
        if (ObjectUtils.isEmpty(processInstanceName)) {
            return processName;
        }
        return processInstanceName.toString();
    }
}
